﻿using KuiHuaBaoDian;

namespace System {

    public interface IReadOnlyByteComplexData : IReadOnlyComplexData<byte>, IReadOnlyByteData { }
    public interface IValueChangedByteComplexData : IReadOnlyByteComplexData, IValueChangedData<byte>, IValueChangedByteData { }
    public interface IByteComplexDataNotifier : IValueChangedByteComplexData, IDataNotifier<byte>, IByteDataNotifier { }
    public interface IByteComplexData : IByteComplexDataNotifier, IComplexData<byte> { }

    public interface IReadOnlySByteComplexData : IReadOnlyComplexData<sbyte>, IReadOnlySByteData { }
    public interface IValueChangedSByteComplexData : IReadOnlySByteComplexData, IValueChangedData<sbyte>, IValueChangedSByteData { }
    public interface ISByteComplexDataNotifier : IValueChangedSByteComplexData, IDataNotifier<sbyte>, ISByteDataNotifier { }
    public interface ISByteComplexData : ISByteComplexDataNotifier, IComplexData<sbyte> { }

    public interface IReadOnlyShortComplexData : IReadOnlyComplexData<short>, IReadOnlyShortData { }
    public interface IValueChangedShortComplexData : IReadOnlyShortComplexData, IValueChangedData<short>, IValueChangedShortData { }
    public interface IShortComplexDataNotifier : IValueChangedShortComplexData, IDataNotifier<short>, IShortDataNotifier { }
    public interface IShortComplexData : IShortComplexDataNotifier, IComplexData<short> { }

    public interface IReadOnlyUShortComplexData : IReadOnlyComplexData<ushort>, IReadOnlyUShortData { }
    public interface IValueChangedUShortComplexData : IReadOnlyUShortComplexData, IValueChangedData<ushort>, IValueChangedUShortData { }
    public interface IUShortComplexDataNotifier : IValueChangedUShortComplexData, IDataNotifier<ushort>, IUShortDataNotifier { }
    public interface IUShortComplexData : IUShortComplexDataNotifier, IComplexData<ushort> { }

    public interface IReadOnlyIntComplexData : IReadOnlyComplexData<int>, IReadOnlyIntData { }
    public interface IValueChangedIntComplexData : IReadOnlyIntComplexData, IValueChangedData<int>, IValueChangedIntData { }
    public interface IIntComplexDataNotifier : IValueChangedIntComplexData, IDataNotifier<int>, IIntDataNotifier { }
    public interface IIntComplexData : IIntComplexDataNotifier, IComplexData<int> { }

    public interface IReadOnlyUIntComplexData : IReadOnlyComplexData<uint>, IReadOnlyUIntData { }
    public interface IValueChangedUIntComplexData : IReadOnlyUIntComplexData, IValueChangedData<uint>, IValueChangedUIntData { }
    public interface IUIntComplexDataNotifier : IValueChangedUIntComplexData, IDataNotifier<uint>, IUIntDataNotifier { }
    public interface IUIntComplexData : IUIntComplexDataNotifier, IComplexData<uint> { }

    public interface IReadOnlyLongComplexData : IReadOnlyComplexData<long>, IReadOnlyLongData { }
    public interface IValueChangedLongComplexData : IReadOnlyLongComplexData, IValueChangedData<long>, IValueChangedLongData { }
    public interface ILongComplexDataNotifier : IValueChangedLongComplexData, IDataNotifier<long>, ILongDataNotifier { }
    public interface ILongComplexData : ILongComplexDataNotifier, IComplexData<long> { }

    public interface IReadOnlyULongComplexData : IReadOnlyComplexData<ulong>, IReadOnlyULongData { }
    public interface IValueChangedULongComplexData : IReadOnlyULongComplexData, IValueChangedData<ulong>, IValueChangedULongData { }
    public interface IULongComplexDataNotifier : IValueChangedULongComplexData, IDataNotifier<ulong>, IULongDataNotifier { }
    public interface IULongComplexData : IULongComplexDataNotifier, IComplexData<ulong> { }

    public interface IReadOnlyFloatComplexData : IReadOnlyComplexData<float>, IReadOnlyFloatData { }
    public interface IValueChangedFloatComplexData : IReadOnlyFloatComplexData, IValueChangedData<float>, IValueChangedFloatData { }
    public interface IFloatComplexDataNotifier : IValueChangedFloatComplexData, IDataNotifier<float>, IFloatDataNotifier { }
    public interface IFloatComplexData : IFloatComplexDataNotifier, IComplexData<float> { }

    public interface IReadOnlyDoubleComplexData : IReadOnlyComplexData<double>, IReadOnlyDoubleData { }
    public interface IValueChangedDoubleComplexData : IReadOnlyDoubleComplexData, IValueChangedData<double>, IValueChangedDoubleData { }
    public interface IDoubleComplexDataNotifier : IValueChangedDoubleComplexData, IDataNotifier<double>, IDoubleDataNotifier { }
    public interface IDoubleComplexData : IDoubleComplexDataNotifier, IComplexData<double> { }

    [Implement(typeof(IReadOnlyByteComplexData))]
    [Implement(typeof(IValueChangedByteComplexData))]
    [Implement(typeof(IByteComplexDataNotifier))]
    [Implement(typeof(IByteComplexData))]
    [Implement(typeof(IReadOnlyComplexData<byte>))]
    [Implement(typeof(IValueChangedComplexData<byte>))]
    [Implement(typeof(IComplexDataNotifier<byte>))]
    [Implement(typeof(IComplexData<byte>))]
    internal sealed class KHBD_ByteComplexData : KHBD_ComplexData<byte>, IByteComplexData {

        protected override byte Calculate() => (byte)(Base.Value * Factor.Value + Additional.Value);

        protected override byte GetDefaultMaxValue() => byte.MaxValue;

        protected override byte GetDefaultMinValue() => byte.MinValue;

        protected override byte Clamp(byte value, byte min, byte max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlySByteComplexData))]
    [Implement(typeof(IValueChangedSByteComplexData))]
    [Implement(typeof(ISByteComplexDataNotifier))]
    [Implement(typeof(ISByteComplexData))]
    [Implement(typeof(IReadOnlyComplexData<sbyte>))]
    [Implement(typeof(IValueChangedComplexData<sbyte>))]
    [Implement(typeof(IComplexDataNotifier<sbyte>))]
    [Implement(typeof(IComplexData<sbyte>))]
    internal sealed class KHBD_SByteComplexData : KHBD_ComplexData<sbyte>, ISByteComplexData {

        protected override sbyte Calculate() => (sbyte)(Base.Value * Factor.Value + Additional.Value);

        protected override sbyte GetDefaultMaxValue() => sbyte.MaxValue;

        protected override sbyte GetDefaultMinValue() => sbyte.MinValue;

        protected override sbyte Clamp(sbyte value, sbyte min, sbyte max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyShortComplexData))]
    [Implement(typeof(IValueChangedShortComplexData))]
    [Implement(typeof(IShortComplexDataNotifier))]
    [Implement(typeof(IShortComplexData))]
    [Implement(typeof(IReadOnlyComplexData<short>))]
    [Implement(typeof(IValueChangedComplexData<short>))]
    [Implement(typeof(IComplexDataNotifier<short>))]
    [Implement(typeof(IComplexData<short>))]
    internal sealed class KHBD_ShortComplexData : KHBD_ComplexData<short>, IShortComplexData {

        protected override short Calculate() => (short)(Base.Value * Factor.Value + Additional.Value);

        protected override short GetDefaultMaxValue() => short.MaxValue;

        protected override short GetDefaultMinValue() => short.MinValue;

        protected override short Clamp(short value, short min, short max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyUShortComplexData))]
    [Implement(typeof(IValueChangedUShortComplexData))]
    [Implement(typeof(IUShortComplexDataNotifier))]
    [Implement(typeof(IUShortComplexData))]
    [Implement(typeof(IReadOnlyComplexData<ushort>))]
    [Implement(typeof(IValueChangedComplexData<ushort>))]
    [Implement(typeof(IComplexDataNotifier<ushort>))]
    [Implement(typeof(IComplexData<ushort>))]
    internal sealed class KHBD_UShortComplexData : KHBD_ComplexData<ushort>, IUShortComplexData {

        protected override ushort Calculate() => (ushort)(Base.Value * Factor.Value + Additional.Value);

        protected override ushort GetDefaultMaxValue() => ushort.MaxValue;

        protected override ushort GetDefaultMinValue() => ushort.MinValue;

        protected override ushort Clamp(ushort value, ushort min, ushort max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyIntComplexData))]
    [Implement(typeof(IValueChangedIntComplexData))]
    [Implement(typeof(IIntComplexDataNotifier))]
    [Implement(typeof(IIntComplexData))]
    [Implement(typeof(IReadOnlyComplexData<int>))]
    [Implement(typeof(IValueChangedComplexData<int>))]
    [Implement(typeof(IComplexDataNotifier<int>))]
    [Implement(typeof(IComplexData<int>))]
    internal sealed class KHBD_IntComplexData : KHBD_ComplexData<int>, IIntComplexData {

        protected override int Calculate() => Base.Value * Factor.Value + Additional.Value;

        protected override int GetDefaultMaxValue() => int.MaxValue;

        protected override int GetDefaultMinValue() => int.MinValue;

        protected override int Clamp(int value, int min, int max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyUIntComplexData))]
    [Implement(typeof(IValueChangedUIntComplexData))]
    [Implement(typeof(IUIntComplexDataNotifier))]
    [Implement(typeof(IUIntComplexData))]
    [Implement(typeof(IReadOnlyComplexData<uint>))]
    [Implement(typeof(IValueChangedComplexData<uint>))]
    [Implement(typeof(IComplexDataNotifier<uint>))]
    [Implement(typeof(IComplexData<uint>))]
    internal sealed class KHBD_UIntComplexData : KHBD_ComplexData<uint>, IUIntComplexData {

        protected override uint Calculate() => Base.Value * Factor.Value + Additional.Value;

        protected override uint GetDefaultMaxValue() => uint.MaxValue;

        protected override uint GetDefaultMinValue() => uint.MinValue;

        protected override uint Clamp(uint value, uint min, uint max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyLongComplexData))]
    [Implement(typeof(IValueChangedLongComplexData))]
    [Implement(typeof(ILongComplexDataNotifier))]
    [Implement(typeof(ILongComplexData))]
    [Implement(typeof(IReadOnlyComplexData<long>))]
    [Implement(typeof(IValueChangedComplexData<long>))]
    [Implement(typeof(IComplexDataNotifier<long>))]
    [Implement(typeof(IComplexData<long>))]
    internal sealed class KHBD_LongComplexData : KHBD_ComplexData<long>, ILongComplexData {

        protected override long Calculate() => Base.Value * Factor.Value + Additional.Value;

        protected override long GetDefaultMaxValue() => long.MaxValue;

        protected override long GetDefaultMinValue() => long.MinValue;

        protected override long Clamp(long value, long min, long max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyULongComplexData))]
    [Implement(typeof(IValueChangedULongComplexData))]
    [Implement(typeof(IULongComplexDataNotifier))]
    [Implement(typeof(IULongComplexData))]
    [Implement(typeof(IReadOnlyComplexData<ulong>))]
    [Implement(typeof(IValueChangedComplexData<ulong>))]
    [Implement(typeof(IComplexDataNotifier<ulong>))]
    [Implement(typeof(IComplexData<ulong>))]
    internal sealed class KHBD_ULongComplexData : KHBD_ComplexData<ulong>, IULongComplexData {

        protected override ulong Calculate() => Base.Value * Factor.Value + Additional.Value;

        protected override ulong GetDefaultMaxValue() => ulong.MaxValue;

        protected override ulong GetDefaultMinValue() => ulong.MinValue;

        protected override ulong Clamp(ulong value, ulong min, ulong max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyFloatComplexData))]
    [Implement(typeof(IValueChangedFloatComplexData))]
    [Implement(typeof(IFloatComplexDataNotifier))]
    [Implement(typeof(IFloatComplexData))]
    [Implement(typeof(IReadOnlyComplexData<float>))]
    [Implement(typeof(IValueChangedComplexData<float>))]
    [Implement(typeof(IComplexDataNotifier<float>))]
    [Implement(typeof(IComplexData<float>))]
    internal sealed class KHBD_FloatComplexData : KHBD_ComplexData<float>, IFloatComplexData {

        protected override float Calculate() => Base.Value * Factor.Value + Additional.Value;

        protected override float GetDefaultMaxValue() => float.MaxValue;

        protected override float GetDefaultMinValue() => float.MinValue;

        protected override float Clamp(float value, float min, float max) => Math.Clamp(value, min, max);
    }

    [Implement(typeof(IReadOnlyDoubleComplexData))]
    [Implement(typeof(IValueChangedDoubleComplexData))]
    [Implement(typeof(IDoubleComplexDataNotifier))]
    [Implement(typeof(IDoubleComplexData))]
    [Implement(typeof(IReadOnlyComplexData<double>))]
    [Implement(typeof(IValueChangedComplexData<double>))]
    [Implement(typeof(IComplexDataNotifier<double>))]
    [Implement(typeof(IComplexData<double>))]
    internal sealed class KHBD_DoubleComplexData : KHBD_ComplexData<double>, IDoubleComplexData {

        protected override double Calculate() => Base.Value * Factor.Value + Additional.Value;

        protected override double GetDefaultMaxValue() => double.MaxValue;

        protected override double GetDefaultMinValue() => double.MinValue;

        protected override double Clamp(double value, double min, double max) => Math.Clamp(value, min, max);
    }

    public interface IReadOnlyComplexData<T> : IReadOnlyData<T>, IEntity {

        new IComplexData<T> AsReadWrite();
        IData<T> IReadOnlyData<T>.AsReadWrite() => throw new NotSupportedException(
            $"{nameof(IReadOnlyData<T>)}.{nameof(IReadOnlyData<T>.AsReadWrite)} " +
            $"已被 {nameof(IReadOnlyComplexData<T>)}.{nameof(AsReadWrite)} 覆盖"
        );

        bool IsDirty { get; }

        IReadOnlyFusionData<T> Base { get; }

        IReadOnlyFusionData<T> Factor { get; }

        IReadOnlyFusionData<T> Additional { get; }
    }

    public interface IValueChangedComplexData<T> : IReadOnlyComplexData<T>, IValueChangedData<T> {
    }

    public interface IComplexDataNotifier<T> : IValueChangedComplexData<T>, IDataNotifier<T> {
    }

    public interface IComplexData<T> : IComplexData<T, IFusionData<T>> { }
    public interface IComplexData<T, TFusion> : IComplexDataNotifier<T> where TFusion : IFusionData<T> {

        void Initialize(T baseOrigin, T factorOrigin, T additionalOrigin);

        new TFusion Base { get; }

        new TFusion Factor { get; }

        new TFusion Additional { get; }

        void Clear();

        void SetMaxValue(T value);

        void SetMinValue(T value);
    }

    public abstract class KHBD_ComplexData<T> : KHBD_ComplexData<T, IFusionData<T>, IData<T>> { }
    public abstract class KHBD_ComplexData<T, TFusion, TData> : KHBD_PoolEntity, IComplexData<T, TFusion> where TFusion : IFusionData<T> where TData : IData<T> {

        public IComplexData<T> AsReadWrite() => this as IComplexData<T>;

        private event IChangable.ChangedDelegate InnerChanged;
        public event IChangable.ChangedDelegate Changed {
            add {
                AssertDispose();
                InnerChanged += value;
            }
            remove {
                AssertDispose();
                InnerChanged -= value;
            }
        }

        private event IValueChangedData<T>.ValueChangedDelegate InnerValueChanged;
        public event IValueChangedData<T>.ValueChangedDelegate ValueChanged {
            add {
                AssertDispose();
                m_CachedValue = Value;
                InnerValueChanged += value;
            }
            remove {
                AssertDispose();
                m_CachedValue = Value;
                InnerValueChanged -= value;
            }
        }

#if UNITY_EDITOR
        private Guid m_GUID;
#endif

        protected override void Allocate() {
#if UNITY_EDITOR
            m_GUID = Guid.NewGuid();
#endif
            m_BaseOrigin.AsReadWrite().SetValueWithOutEvent(GetDefaultValue());
            m_Base.AddWithOutEvent(m_BaseOrigin);

            m_FactorOrigin.AsReadWrite().SetValueWithOutEvent(GetDefaultValue());
            m_Factor.AddWithOutEvent(m_FactorOrigin);

            m_AdditionalOrigin.AsReadWrite().SetValueWithOutEvent(GetDefaultValue());
            m_Additional.AddWithOutEvent(m_AdditionalOrigin);

            MaxValue = GetDefaultMaxValue();
            MinValue = GetDefaultMinValue();
            IsDirty = true;
        }

        protected override void Release() {
            if (InnerChanged != null) {
                InnerChanged = null;
            }
            if (InnerValueChanged != null) {
                InnerValueChanged = null;
            }
            m_CachedValue = GetDefaultValue();
            IsDirty = false;
        }

        [Autowired]
        private readonly TData m_BaseOrigin;
        [Autowired]
        private readonly TFusion m_Base;
        public IReadOnlyFusionData<T> Base {
            get {
                AssertDispose();
                return m_Base;
            }
        }
        TFusion IComplexData<T, TFusion>.Base {
            get {
                AssertDispose();
                return m_Base;
            }
        }

        [Autowired]
        private readonly TData m_FactorOrigin;
        [Autowired]
        private readonly TFusion m_Factor;
        public IReadOnlyFusionData<T> Factor {
            get {
                AssertDispose();
                return m_Factor;
            }
        }
        TFusion IComplexData<T, TFusion>.Factor {
            get {
                AssertDispose();
                return m_Factor;
            }
        }

        [Autowired]
        private readonly TData m_AdditionalOrigin;
        [Autowired]
        private readonly TFusion m_Additional;
        public IReadOnlyFusionData<T> Additional {
            get {
                AssertDispose();
                return m_Additional;
            }
        }
        TFusion IComplexData<T, TFusion>.Additional {
            get {
                AssertDispose();
                return m_Additional;
            }
        }

        private T m_CachedValue;
        public T Value {
            get {
                AssertDispose();
                if (IsDirty) {
                    IsDirty = false;
                    m_CachedValue = GetDefaultValue();
                    m_CachedValue = Calculate();
                    m_CachedValue = Clamp(m_CachedValue, MinValue, MaxValue);
                }
                return m_CachedValue;
            }
        }

        protected virtual T GetDefaultValue() => default;

        protected T MaxValue { get; private set; }
        protected abstract T GetDefaultMaxValue();
        public void SetMaxValue(T value) {
            AssertDispose();
            MaxValue = value;
            Dirty();
        }

        protected T MinValue { get; private set; }
        protected abstract T GetDefaultMinValue();
        public void SetMinValue(T value) {
            AssertDispose();
            MinValue = value;
            Dirty();
        }

        protected abstract T Calculate();

        protected abstract T Clamp(T value, T min, T max);

        public void Initialize(T baseOrigin, T factorOrigin, T additionalOrigin) {
            AssertDispose();

            // 未初始化前不订阅事件，避免多次触发，且未初始化前的值变更也无意义
            m_Base.Changed -= Data_Changed;
            m_Factor.Changed -= Data_Changed;
            m_Additional.Changed -= Data_Changed;
            m_BaseOrigin.Value = baseOrigin;
            m_FactorOrigin.Value = factorOrigin;
            m_AdditionalOrigin.Value = additionalOrigin;
            m_Base.Changed += Data_Changed;
            m_Factor.Changed += Data_Changed;
            m_Additional.Changed += Data_Changed;
            Dirty();
        }

        public void Clear() {
            AssertDispose();
            if (DoClear()) {
                Dirty();
            }
        }
        private bool DoClear() {
            var isDirty = false;
            if (m_Base.Count > 1) {
                m_Base.ClearWithOutEvent();
                m_Base.AddWithOutEvent(m_BaseOrigin);
                isDirty = true;
            }
            if (m_Factor.Count > 1) {
                m_Factor.ClearWithOutEvent();
                m_Factor.AddWithOutEvent(m_FactorOrigin);
                isDirty = true;
            }
            if (m_Additional.Count > 1) {
                m_Additional.ClearWithOutEvent();
                m_Additional.AddWithOutEvent(m_AdditionalOrigin);
                isDirty = true;
            }
            return isDirty;
        }

        public bool IsDirty { get; private set; }

        private void Data_Changed() {
            Dirty();
        }

        private void Dirty() {
            IsDirty = true;
            Notify();
        }

        private void Notify() {
            if (KHBD.Context.IsApplicationQuit) {
                return;
            }
            InnerValueChanged?.Invoke(m_CachedValue, Value);
            InnerChanged?.Invoke();
        }

        void IDataNotifier<T>.NotifyChangedEvent() {
            AssertDispose();
            if (KHBD.Context.IsApplicationQuit) {
                return;
            }
            InnerChanged?.Invoke();
        }

        void IDataNotifier<T>.NotifyValueChangedEvent() {
            AssertDispose();
            Notify();
        }

        public override string ToString() => Value.ToString();
    }
}